import { OlUtil } from '../index'
import { Map, Feature } from 'ol'
import VectorLayer from 'ol/layer/Vector'
import VectorSource from 'ol/source/Vector'
import Point from 'ol/geom/Point'
import { Fill, Stroke, Style } from 'ol/style'
import { Kriging } from './kriging'
import { isobands, featureCollection, intersect, polygon as turfPolygon } from '@turf/turf'
import { GeoJSON } from 'ol/format'
import { Coordinate } from 'ol/coordinate'

export interface ComputeIsosurfacesOptions {
  /**
   * 矢量图层ID
   */
  id: string
  /**
   * 克里金模型类型
   * @type {'gaussian'|'exponential'|'spherical'}
   */
  krigingModel: 'gaussian' | 'exponential' | 'spherical'
  /**
   * 测量误差方差
   */
  krigingSigma2: number
  /**
   * 克里金模型参数
   */
  krigingAlpha: number
  /**
   * 等值面颜色数组
   */
  colors: Array<string>
  /**
   * 网格密度
   */
  gridDensity: number
  /**
   * 裁剪区域
   */
  province: Array<Coordinate>
  /**
   * 点位数值的字段名称
   */
  valueField: string
}
/**
 * 等值面工具类，用于基于点数据生成等值面图层
 * 使用克里金插值算法和Turf.js的等值面生成功能
 */
export class Isosurfaces {
  private map: Map | null = null // OpenLayers地图实例
  private olutil: OlUtil | null = null // OlUtil工具类实例
  private selectedFeatures: Array<any> = [] // 选中的要素集合
  private pointArr: Array<any> = [] // 点数据数组

  /**
   * 构造函数，初始化等值面工具
   * @param {Map} map - OpenLayers地图实例
   * @param {OlUtil} olutil - OlUtil工具类实例
   */
  constructor(map: Map, olutil: OlUtil) {
    this.map = map
    this.olutil = olutil
  }

  /**设置传入的点数据
   * @param {Array<Object>} pointArr - 点数据数组，包含x,y坐标和z值
   */
  setPointArr = (pointArr: Array<any>) => {
    if (!this.map || !this.olutil) return
    this.pointArr = pointArr
  }

  /**
   * 计算并生成等值面图层
   * 1. 创建临时矢量图层
   * 2. 使用克里金插值算法生成网格数据
   * 3. 使用Turf.js生成等值面
   * 4. 将结果添加到地图
   * @param {Array<Object>} _pointArr - 点数据数组，包含x,y坐标和z值
   * @param {Object} _params - 参数对象，包含克里金模型、测量误差方差、克里金模型参数、等值面颜色数组、网格密度和裁剪区域等
   * @returns {VectorLayer|void} - 异步操作完成后返回Promise
   */
  computeIsosurfaces = (_pointArr:Array<any> ,_params?: Partial<ComputeIsosurfacesOptions>): VectorLayer | void => {
    this.setPointArr(_pointArr)
    if (!this.map || !this.olutil) return
    const defalutParams: ComputeIsosurfacesOptions = {
      krigingModel: 'exponential', // 克里金模型类型 'gaussian' - 高斯模型 'exponential' - 指数模型 'spherical' - 球面模型
      krigingSigma2: 0.01, // 测量误差方差(通常设置在 0.1 到 10 之间，值越小表示对原始数据的拟合越精确（可能过拟合）值越大表示允许更多平滑（可能欠拟合）)
      krigingAlpha: 0.5, // 克里金模型参数 值越大表示空间相关性衰减越快（影响范围小） 值越小表示空间相关性衰减越慢（影响范围大）
      colors: [
        'rgba(230, 245, 224, 0.9)', // 微量降雨 (浅绿)
        'rgba(161, 217, 155, 0.9)', // 小雨 (绿)
        'rgba(102, 189, 99, 0.9)', // 中雨 (深绿)
        'rgba(65, 182, 196, 0.9)', // 中到大雨 (蓝绿)
        'rgba(66, 165, 245, 0.9)', // 大雨 (蓝)
        'rgba(100, 149, 237, 0.9)', // 暴雨 (深蓝)
        'rgba(147, 112, 219, 0.9)', // 大暴雨 (蓝紫)
        'rgba(186, 104, 200, 0.9)', // 特大暴雨 (紫)
        'rgba(171, 71, 188, 0.9)', // 极端降雨 (深紫)
        'rgba(142, 36, 170, 0.9)' // 超强降雨 (紫红)
      ],
      gridDensity: 100, // 网格密度 (每米的格点数)
      province: [], // 裁剪区域
      id: 'isosurfaces', // 矢量图层ID
      valueField: 'value' // 点位数值的字段名称
    }
    const params: ComputeIsosurfacesOptions = Object.assign({}, defalutParams, _params)

    let WFSVectorSource = new VectorSource()
    let WFSVectorLayer = new VectorLayer({
      source: WFSVectorSource
    })
    this.map.addLayer(WFSVectorLayer)
    let pointArr
    if (this.pointArr && this.pointArr.length > 0) {
      pointArr = this.pointArr
    } else {
      pointArr = [
        { x: 87.62, y: 43.83, z: 5 }, // 小雨
        { x: 87.63, y: 43.84, z: 15 }, // 中雨
        { x: 87.61, y: 43.82, z: 2 }, // 毛毛雨
        { x: 87.625, y: 43.835, z: 25 }, // 中到大雨
        { x: 87.615, y: 43.825, z: 40 }, // 大雨
        { x: 87.64, y: 43.85, z: 60 }, // 暴雨
        { x: 87.6, y: 43.8, z: 80 }, // 大暴雨
        { x: 87.635, y: 43.845, z: 100 }, // 特大暴雨
        { x: 87.605, y: 43.815, z: 120 }, // 极端降雨
        { x: 87.645, y: 43.855, z: 150 } // 超强降雨
      ]
    }

    // 构造面数据
    for (let i = 0; i < pointArr.length; i++) {
      let feature = new Feature({
        geometry: new Point([pointArr[i].x, pointArr[i].y]),
        value: pointArr[i][params.valueField]
      })
      WFSVectorSource.addFeature(feature)
    }
    let extent = WFSVectorLayer!.getSource()!.getExtent()
    WFSVectorSource.forEachFeatureIntersectingExtent(extent, (feature) => {
      this.selectedFeatures.push(feature)
    })

    // 计算等值面
    let values: Array<number> = [],
      lngs: Array<number> = [],
      lats: Array<number> = []
    this.selectedFeatures.forEach((feature) => {
      values.push(feature.values_.value)
      lngs.push(feature.values_.geometry.flatCoordinates[0])
      lats.push(feature.values_.geometry.flatCoordinates[1])
    })

    const kriging = new Kriging()
    let variogram = kriging.train(values, lngs, lats, params.krigingModel, params.krigingSigma2, params.krigingAlpha)
    let polygons = []
    polygons.push([
      [extent[0], extent[1]],
      [extent[0], extent[3]],
      [extent[2], extent[3]],
      [extent[2], extent[1]]
    ])
    let grid = kriging.grid(polygons, variogram, (extent[2] - extent[0]) / params.gridDensity) // 调整网格密度

    if (WFSVectorLayer !== null) {
      this.map.removeLayer(WFSVectorLayer)
    }

    // 检查grid是否为有效对象
    if (!grid || typeof grid !== 'object' || !grid.list || !Array.isArray(grid.list)) {
      console.error('Invalid grid data:', grid)
      return
    }

    // 获取最大和最小值
    const minValue = Math.min(...values)
    const maxValue = Math.max(...values)
    // 根据值获取颜色
    function getColorForValue(value: string, minValue: number, maxValue: number): string {
      const numValue = Math.round(parseFloat(value.split('-')[1]))
      // 确保index在合理范围内
      const index = Math.min(
        Math.max(0, Math.floor(((numValue - minValue) / (maxValue - minValue)) * params.colors.length)),
        params.colors.length - 1
      )
      return params.colors[index] || params.colors[0] // 默认返回第一个颜色
    }
    let vectorSource = new VectorSource()
    WFSVectorLayer = new VectorLayer({
      zIndex: 10,
      source: vectorSource,
      style: function (feature) {
        const value = feature.get('value')
        // 根据值设置不同颜色
        const color = getColorForValue(value, minValue, maxValue)
        return new Style({
          fill: new Fill({
            color: color
          }),
          // 使用与填充色相同的颜色避免相邻区域出现黑边
          stroke: new Stroke({
            color: color.replace(/[\d\.]+\)$/, '1)'), // 移除透明度
            width: 0.5 // 使用更细的线宽
          })
        })
      }
    })

    // 直接使用grid数据创建点网格
    const features = grid.list
      .map((row, rowIndex) => {
        return row.map((value, colIndex) => {
          const x = extent[0] + (colIndex * (extent[2] - extent[0])) / (row.length - 1)
          const y = extent[1] + (rowIndex * (extent[3] - extent[1])) / (grid.list.length - 1)
          return {
            type: 'Feature',
            geometry: {
              type: 'Point',
              coordinates: [x, y]
            },
            properties: {
              value: value
            }
          }
        })
      })
      .flat()

    const pointGridData: any = {
      type: 'FeatureCollection',
      features: features
    }

    // 确保断点与数据范围匹配
    let breaks = []
    for (let i = 0; i <= 10; i++) {
      breaks.push(minValue + ((maxValue - minValue) * i) / 10)
    }
    let _isobands = isobands(pointGridData, breaks, { zProperty: 'value' })

    let polyFeatures
    if (!params.province || params.province.length === 0) {
      polyFeatures = new GeoJSON().readFeatures(_isobands, {
        featureProjection: 'EPSG:4326'
      })
    } else {
      const clippedFeatures = _isobands.features
        .map((feature) => {
          const provinceFeature: any = turfPolygon([params.province])
          const clipped = intersect(featureCollection([feature, provinceFeature]))
          if (clipped) {
            clipped.properties = feature.properties
          }
          return clipped
        })
        .filter(Boolean) // 过滤掉null结果
      const _clippedFeatures = {
        type: 'FeatureCollection',
        features: clippedFeatures
      }
      polyFeatures = new GeoJSON().readFeatures(_clippedFeatures, {
        featureProjection: 'EPSG:4326'
      })
    }

    vectorSource.addFeatures(polyFeatures)

    WFSVectorLayer.set('id', params.id)
    this.map.addLayer(WFSVectorLayer)
    return WFSVectorLayer
  }
}
